package com.example.redis_redisson_demo.config;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.core.AppenderBase;
import com.google.common.util.concurrent.AtomicLongMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 自定义的一个LogAppender
 * 希望统计日志中发生Error的次数，如果在每分钟Error数超过3，则触发处理程序
 * 必须要继承 AppenderBase<ILoggingEvent> 并实现append方法
 */
public class RedisLogAppender extends AppenderBase<ILoggingEvent> {

    //  使用guava的AtomicLongMap,用于并发计数
    private final AtomicLongMap<String> ATOMIC_LONG_MAP = AtomicLongMap.create();

    private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());

    /**
     * 继承并实现的方法。输入的 iLoggingEvent 就是日志事件。
     * append中的内容就是对日志事件进行的处理。
     * 由于XML中已经设置过滤器只放过ERROR级别，所以到这里的都是ERROR
     *
     * @param iLoggingEvent
     */
    @Override
    protected void append(ILoggingEvent iLoggingEvent) {
        // 日志事件提供获取事件异常的接口。
        // 因为有的ERROR日志可能是我们自己调用的，例如 LOGGER.ERROR("数据主键为空")。
        // 有的则是抛出异常时日志框架自动记录的ERROR。
        // 前者情况下，iLoggingEvent.getThrowableProxy() 为空，后者iLoggingEvent.getThrowableProxy()将获得同时抛出的异常
        IThrowableProxy throwableProxy = iLoggingEvent.getThrowableProxy();

        // 合法调用的 LOGGER.ERROR 直接放过
        if(throwableProxy != null){
            // 获取分钟戳
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
            String key = dateFormat.format(new Date());
            // 该分钟错误数+1后返回
            long errorInOneMin = ATOMIC_LONG_MAP.incrementAndGet(key);
            if (errorInOneMin > 2) {
                // ERROR数量超过2，触发报警，可以在这里写相应的处理程序，例如向管理员发送邮件或者发送代码
                LOGGER.warn("一分钟内出现3次未定义异常，已通知管理员。异常信息:" + throwableProxy.getMessage());
            }
            // 清空历史数据
            for (String oldKey : ATOMIC_LONG_MAP.asMap().keySet()) {
                if (!oldKey.equals(key)) {
                    ATOMIC_LONG_MAP.remove(oldKey);
                }
            }
        }
    }
}
